package common.start;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.*;
import java.lang.management.ManagementFactory;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

/**
 *
 *
 * CreateBy: <a href="wangjunneil@gmail.com">Calvin</a>
 * CreateTime: 2013-12-06 10:05
 */
public final class Bootstrap {

    /**
     * 日志对象引用
     */
    private static final Log log = LogFactory.getLog(Bootstrap.class);

    /**
     * home.dir为用户在虚拟机中手动通过-Dhome.dir="XXX"指定主程序目录，若没有在虚拟机参数中发现
     * 此配置则程序自动获取主程序目录
     */
    private static final String THE_HOME_KEY = "home.dir";

    /**
     * 引导对象引用
     */
    private static Bootstrap daemon = null;

    /**
     * 配置文件对象
     */
    private BootConfig bootConfig = null;

    /**
     * 类加载器对象
     */
    private ClassLoader classLoader = null;

    /**
     * 主程序对象引用
     */
    private Object runDaemon = null;

    /**
     * 是否允许启动
     */
    private boolean allowRun = false;

    // ---------------------------------------------------------------------------------------- Construct Method

    /**
     * 私有的构造方法
     */
    private Bootstrap() { }

    // ---------------------------------------------------------------------------------------- Public Method

    /**
     * 初始化方法入口
     */
    private void initialize() throws Exception {
        setHomeBase();
        printInformation();

        initializeConfiguration();

        initClassLoader();
        Thread.currentThread().setContextClassLoader(this.classLoader);

        startDaemonServer();

        createStartupInstance();
    }

    /**
     * 打印当前环境信息
     */
    private void printInformation() {
        StringBuilder buffer = new StringBuilder();
        buffer.append(System.getProperty("java.vendor"));
        buffer.append(" ");
        buffer.append(System.getProperty("java.version"));
        buffer.append(" ");
        buffer.append(System.getProperty("java.home"));
        log.info("Java Runtime: " + buffer.toString());

        buffer = new StringBuilder();
        buffer.append("current=");
        buffer.append(Runtime.getRuntime().totalMemory() / 1024L);
        buffer.append("k  free=");
        buffer.append(Runtime.getRuntime().freeMemory() / 1024L);
        buffer.append("k  max=");
        buffer.append(Runtime.getRuntime().maxMemory() / 1024L);
        buffer.append("k");
        log.info("Heap sizes: " + buffer.toString());

        List jvmArgs = ManagementFactory.getRuntimeMXBean().getInputArguments();
        buffer = new StringBuilder();
        for (Iterator i$ = jvmArgs.iterator(); i$.hasNext(); ) { Object arg = i$.next();
            buffer.append(" ").append(arg);
        }
        log.info("JVM args:" + buffer.toString());

        log.info("APP_HOME: " + System.getProperty(THE_HOME_KEY));
    }

    /**
     * 初始化读取clasthpath路径中的config.properties配置文件
     *
     * @throws IOException
     */
    private void initializeConfiguration() throws IOException {
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("server.properties");
        Properties properties = new Properties();
        properties.load(inputStream);

        bootConfig = new BootConfig();
        bootConfig.setVersion(properties.getProperty("version"));
        bootConfig.setRunClass(properties.getProperty("runclass"));
        bootConfig.setDaemonPort(Integer.parseInt(properties.getProperty("daemonport")));
    }

    /**
     * 设置引导程序目录
     */
    private void setHomeBase() {
        if (System.getProperty(THE_HOME_KEY) != null) return;

        StringBuffer bootstrapJarFileName = new StringBuffer("bootstrap-");
        bootstrapJarFileName.append(bootConfig.getVersion()).append(".jar");

        File bootstrapJar = new File(System.getProperty("user.dir"), bootstrapJarFileName.toString());
        if (!bootstrapJar.exists()) System.setProperty(THE_HOME_KEY, System.getProperty("user.dir"));
        try {
            System.setProperty(THE_HOME_KEY, new File(System.getProperty("user.dir"), "..").getCanonicalPath());
        } catch (Exception e) {
            System.setProperty(THE_HOME_KEY, System.getProperty("user.dir"));
        }
    }

    /**
     * 初始化类加载器对象
     *
     * @throws IOException
     */
    private void initClassLoader() throws IOException {
        this.classLoader = createClassLoader();
        if (this.classLoader == null) this.classLoader = this.getClass().getClassLoader();
    }

    /**
     * 创建类加载器对象
     *
     * @return
     * @throws IOException
     */
    private ClassLoader createClassLoader() throws IOException {
        Set<URL> set = new LinkedHashSet<URL>();

        // lib目录
        File libDirectory = new File(System.getProperty(THE_HOME_KEY), "lib");
        libDirectory = libDirectory.getCanonicalFile();
        URL directoryUrl = libDirectory.toURI().toURL();
        set.add(directoryUrl);

        // lib目录下Jar包
        File [] jarFiles = libDirectory.listFiles(new FileFilter() {
            @Override
            public boolean accept(File file) {
                if (file.getName().endsWith(".jar")) return true;
                return false;
            }
        });
        URL jarFileUrl = null;
        for (File jarFile : jarFiles) {
            jarFileUrl = jarFile.toURI().toURL();
            set.add(jarFileUrl);
        }

        URL[] array = (URL[])set.toArray(new URL[set.size()]);
        StandardClassLoader standardClassLoader = new StandardClassLoader(array);

        return standardClassLoader;
    }

    /**
     * 启动后台服务实例
     */
    private void startDaemonServer() {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                Thread.currentThread().setName("DaemonServer");
                DaemonServer server = new DaemonServer(bootConfig);
                try {
                    server.start();
                } catch (Exception e) {
                    log.error(e.toString());
                }
            }
        };

        new Thread(runnable).start();
    }

    /**
     * 创建启动实例对象
     */
    private void createStartupInstance() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        Class startupClass = this.classLoader.loadClass(bootConfig.getRunClass());
        Object startupInstance = startupClass.newInstance();
        this.runDaemon = startupInstance;
    }

    /**
     * 处理程序命令行参数
     *
     * @param args
     */
    private void processArguments(String[] args) throws Exception {
        String command = "start";
        if (args.length > 0) {
            command = args[(args.length - 1)];
        }

        if (command.equals("start")) {
            allowRun = true;
        } else if (command.equals("stop")) {
            initializeConfiguration();
            DaemonServer daemonServer = new DaemonServer(bootConfig);
            daemonServer.stop();
        } else {
            log.warn("Bootstrap: command \"" + command + "\" does not exist.");
        }
    }

    /**
     * 启动后台服务
     */
    private void startDaemon() throws Exception {
        Method method = runDaemon.getClass().getDeclaredMethod("startWork");
        method.invoke(runDaemon);
    }

    // ---------------------------------------------------------------------------------------- Main Method

    /**
     * 主程序入口
     *
     * @param args 主程序命令行参数
     */
    public static void main(String[] args) {
        if (daemon == null) {
            daemon = new Bootstrap();
            try {
                daemon.processArguments(args);
                if (daemon.allowRun) {
                    daemon.initialize();
                    daemon.startDaemon();
                }
            } catch (Throwable th) {
                log.error(th.toString(), th);

                System.exit(1); // 程序非正常退出
            }
        }
    } // end of main method



} ///:~
